package com.jingyuyao.tactical;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.LifecycleListener;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter;
import com.badlogic.gdx.utils.Array;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

@SuppressWarnings("GDXJavaStaticResource")
public class BitmapFonts implements LifecycleListener {

    protected static final BitmapFonts instance;
    private static final Map<FontKey, BitmapFont> fontMap = new ConcurrentHashMap<>();
    private static FreeTypeFontGenerator fontGenerator;

    static {
        instance = new BitmapFonts();
        Gdx.app.addLifecycleListener(instance);
    }

    private BitmapFonts() {
        setDefaultTtfFilePath("fonts/MapleMono-SC-NF-Regular.ttf");
    }

    public static void setDefaultTtfFilePath(String defaultTtfFilePath) {
        fontGenerator = new FreeTypeFontGenerator(Gdx.files.internal(defaultTtfFilePath));
    }

    public static BitmapFont of(int size) {
        return of(size, Color.WHITE);
    }

    public static BitmapFont of(int size, Color color) {
        FreeTypeFontParameter parameter = new FreeTypeFontParameter();
        parameter.size = size;
        parameter.color = color;
        return of(parameter);
    }

    public static BitmapFont of(FreeTypeFontParameter parameter) {
        FontKey fontKey = new FontKey(parameter);
        if (!fontMap.containsKey(fontKey)) {
            // BitmapFont font = new LazyBitmapFont(fontGenerator, parameter);
            final boolean hd = true;
            FreeTypeFontParameter hdParameter = parameter;
            if (hd) {
                hdParameter = FontKey.clone(parameter);
                hdParameter.size *= 2f;
                hdParameter.borderWidth *= 2f;
                hdParameter.shadowOffsetX *= 2f;
                hdParameter.shadowOffsetY *= 2f;
                hdParameter.spaceX *= 2f;
                hdParameter.spaceY *= 2f;
                hdParameter.padTop *= 2f;
                hdParameter.padLeft *= 2f;
                hdParameter.padBottom *= 2f;
                hdParameter.padRight *= 2f;
            }
            BitmapFont font = instance.newBitmapFont(hdParameter);
            if (hd) {
                font.getData().setScale(1 / 2f);
            }
            fontMap.put(fontKey, font);
        }
        return fontMap.get(fontKey);
    }

    protected BitmapFont newBitmapFont(FreeTypeFontParameter param) {
        param.incremental = true;
        param.characters = "howdY\u0000";

        FreeTypeFontGenerator.setMaxTextureSize(128);
        FreeTypeFontGenerator.FreeTypeBitmapFontData data = new FreeTypeFontGenerator.FreeTypeBitmapFontData() {
            public int getWrapIndex(Array<BitmapFont.Glyph> glyphs, int start) {
                return DynamicBitmapFont.SimplifiedChinese.getWrapIndex(glyphs, start);
            }
        };

        // By default latin chars are used for x and cap height, causing some fonts to display non-latin chars out of bounds.
        data.xChars = new char[]{'动'};
        data.capChars = new char[]{'动'};

        return fontGenerator.generateFont(param, data);
    }

    @Override
    public void pause() {

    }

    @Override
    public void resume() {

    }

    @Override
    public void dispose() {
        for (BitmapFont font : fontMap.values()) {
            font.dispose();
        }
        fontMap.clear();
        fontGenerator.dispose();
    }

    public static class FontKey {

        private FreeTypeFontParameter _this;

        public FontKey(FreeTypeFontParameter _this) {
            this._this = _this;
        }

        public static FreeTypeFontParameter clone(FreeTypeFontParameter original) {
            FreeTypeFontParameter copy = new FreeTypeFontParameter();
            copy.size = original.size;
            copy.mono = original.mono;
            copy.hinting = original.hinting;
            copy.color = new Color(original.color);
            copy.gamma = original.gamma;
            copy.renderCount = original.renderCount;
            copy.borderWidth = original.borderWidth;
            copy.borderColor = new Color(original.borderColor);
            copy.borderStraight = original.borderStraight;
            copy.borderGamma = original.borderGamma;
            copy.shadowOffsetX = original.shadowOffsetX;
            copy.shadowOffsetY = original.shadowOffsetY;
            copy.shadowColor = new Color(original.shadowColor);
            copy.spaceX = original.spaceX;
            copy.spaceY = original.spaceY;
            copy.padTop = original.padTop;
            copy.padLeft = original.padLeft;
            copy.padBottom = original.padBottom;
            copy.padRight = original.padRight;
            copy.characters = original.characters;
            copy.kerning = original.kerning;
            // Handle PixmapPacker carefully if it's not null (currently null for simplicity).
            copy.packer = null;
            copy.flip = original.flip;
            copy.genMipMaps = original.genMipMaps;
            copy.minFilter = original.minFilter;
            copy.magFilter = original.magFilter;
            copy.incremental = original.incremental;

            return copy;
        }

        /**
         * 将 FreeTypeFontParameter 对象重置为默认值。
         *
         * @param param 要重置的 FreeTypeFontParameter 对象。此对象将被修改，
         *              设置各种字体属性的默认值，如大小、颜色、微调、
         *              边框、阴影、内边距和纹理过滤选项。
         */
        public static void reset(FreeTypeFontParameter param) {
            param.size = 16;
            param.mono = false;
            param.hinting = FreeTypeFontGenerator.Hinting.AutoMedium;
            param.color = new Color(Color.WHITE);
            param.gamma = 1.8f;
            param.renderCount = 2;
            param.borderWidth = 0;
            param.borderColor = new Color(Color.BLACK);
            param.borderStraight = false;
            param.borderGamma = 1.8f;
            param.shadowOffsetX = 0;
            param.shadowOffsetY = 0;
            param.shadowColor = new Color(0, 0, 0, 0.75f);
            param.spaceX = 0;
            param.spaceY = 0;
            param.padTop = 0;
            param.padLeft = 0;
            param.padBottom = 0;
            param.padRight = 0;
            param.characters = FreeTypeFontGenerator.DEFAULT_CHARS;
            param.kerning = true;
            param.packer = null;
            param.flip = false;
            param.genMipMaps = false;
            param.minFilter = Texture.TextureFilter.Nearest;
            param.magFilter = Texture.TextureFilter.Nearest;
            param.incremental = false;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            FontKey fontKey = (FontKey) o;

            if (_this.size != fontKey._this.size) return false;
            if (_this.mono != fontKey._this.mono) return false;
            if (!Objects.equals(_this.color, fontKey._this.color)) return false;
            if (Float.compare(fontKey._this.gamma, _this.gamma) != 0) return false;
            if (_this.renderCount != fontKey._this.renderCount) return false;
            if (Float.compare(fontKey._this.borderWidth, _this.borderWidth) != 0) return false;
            if (!Objects.equals(_this.borderColor, fontKey._this.borderColor)) return false;
            if (_this.borderStraight != fontKey._this.borderStraight) return false;
            if (Float.compare(fontKey._this.borderGamma, _this.borderGamma) != 0) return false;
            if (_this.shadowOffsetX != fontKey._this.shadowOffsetX) return false;
            if (_this.shadowOffsetY != fontKey._this.shadowOffsetY) return false;
            if (_this.spaceX != fontKey._this.spaceX) return false;
            if (_this.spaceY != fontKey._this.spaceY) return false;

            if (_this.padTop != fontKey._this.padTop) return false;
            if (_this.padLeft != fontKey._this.padLeft) return false;
            if (_this.padBottom != fontKey._this.padBottom) return false;
            if (_this.padRight != fontKey._this.padRight) return false;
            // if (_this.kerning != fontKey._this.kerning) return false;
            if (_this.flip != fontKey._this.flip) return false;
            if (_this.genMipMaps != fontKey._this.genMipMaps) return false;

            if (_this.hinting != fontKey._this.hinting) return false;
            if (!Objects.equals(_this.shadowColor, fontKey._this.shadowColor)) return false;
            if (!Objects.equals(_this.packer, fontKey._this.packer)) return false;
            if (_this.minFilter != fontKey._this.minFilter) return false;

            return _this.magFilter == fontKey._this.magFilter;
        }

        @Override
        public int hashCode() {
            int result = _this.size;
            result = 31 * result + (_this.mono ? 1 : 0);
            result = 31 * result + (_this.color != null ? _this.color.hashCode() : 0);
            result = 31 * result + (_this.gamma != +0.0f ? Float.floatToIntBits(_this.gamma) : 0);
            result = 31 * result + _this.renderCount;
            result = 31 * result + (_this.borderWidth != +0.0f ? Float.floatToIntBits(_this.borderWidth) : 0);
            result = 31 * result + (_this.borderColor != null ? _this.borderColor.hashCode() : 0);
            result = 31 * result + (_this.borderStraight ? 1 : 0);
            result = 31 * result + (_this.borderGamma != +0.0f ? Float.floatToIntBits(_this.borderGamma) : 0);
            result = 31 * result + _this.shadowOffsetX;
            result = 31 * result + _this.shadowOffsetY;
            result = 31 * result + _this.spaceX;
            result = 31 * result + _this.spaceY;

            result = 31 * result + _this.padTop;
            result = 31 * result + _this.padLeft;
            result = 31 * result + _this.padBottom;
            result = 31 * result + _this.padRight;
            // result = 31 * result + (_this.kerning ? 1 : 0);
            result = 31 * result + (_this.flip ? 1 : 0);
            result = 31 * result + (_this.genMipMaps ? 1 : 0);

            result = 31 * result + (_this.hinting != null ? _this.hinting.hashCode() : 0);
            result = 31 * result + (_this.shadowColor != null ? _this.shadowColor.hashCode() : 0);
            result = 31 * result + (_this.packer != null ? _this.packer.hashCode() : 0);
            result = 31 * result + (_this.minFilter != null ? _this.minFilter.hashCode() : 0);

            result = 31 * result + (_this.magFilter != null ? _this.magFilter.hashCode() : 0);
            return result;
        }
    }
}
